package com.binzaijun.stock.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.binzaijun.stock.common.StockChangeEnum;
import com.binzaijun.stock.constant.Constants;
import com.binzaijun.stock.domain.*;
import com.binzaijun.stock.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 股票数据服务
 * 替代原来的StockDataUtil工具类,使用Spring管理
 */
@Service
public class StockDataService {

    private static final Logger log = LoggerFactory.getLogger(StockDataService.class);

    private static final BigDecimal PERCENTAGE_10 = new BigDecimal("1.1");
    private static final BigDecimal BIG_DECIMAL_MAX = new BigDecimal("999999");
    private static final BigDecimal BIG_DECIMAL_ZERO = BigDecimal.ZERO;

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private StockStringService stockStringService;

    @Value("${stock.data.length:60}")
    private int dataLen;

    @Value("${stock.scale:240}")
    private int scale;

    @Value("${stock.ma:5,10,20}")
    private String ma;

    @Value("${stock.api.sina.history:https://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData}")
    private String historyStockDataOfSinaURL;

    @Value("${stock.api.qt.realtime:http://qt.gtimg.cn/q=}")
    private String realTimeStockDataOfQtURL;

    @Value("${stock.api.eastmoney.changes:http://push2ex.eastmoney.com/getAllStockChanges}")
    private String stockChangesDataOfEastMoney;

    /**
     * 获取新浪股票历史数据
     */
    public List<SinaStock> getSinaStockData(String symbol) {
        try {
            URI uri = UriComponentsBuilder.fromUriString(historyStockDataOfSinaURL)
                    .queryParam(Constants.DATALEN, dataLen)
                    .queryParam(Constants.SCALE, scale)
                    .queryParam(Constants.MA, ma)
                    .queryParam(Constants.SYMBOL, symbol)
                    .build(true)
                    .toUri();

            String body = restTemplate.getForObject(uri, String.class);
            log.debug("获取新浪股票数据成功: symbol={}", symbol);
            return Optional.ofNullable(JSON.parseArray(body, SinaStock.class)).orElseGet(ArrayList::new);
        } catch (Exception e) {
            log.error("获取新浪股票数据失败: symbol={}", symbol, e);
            return Collections.emptyList();
        }
    }

    /**
     * 获取股票最大连板天数
     */
    public int getConsecutiveDaysOfStockPriceLimitUp(BigDecimal[][] stocks) {
        if (stocks.length <= 1) return 0;

        int res = 0, day = 0;
        for (int i = 1; i < stocks.length; i++) {
            BigDecimal expected = stocks[i - 1][3].multiply(PERCENTAGE_10).setScale(2, RoundingMode.HALF_UP);
            day = expected.compareTo(stocks[i][3]) == 0 ? day + 1 : 0;
            res = Math.max(res, day);
        }
        return res;
    }

    /**
     * 股票涨停天数
     */
    public int getDaysOfStockPriceLimitUp(BigDecimal[][] stocks) {
        if (stocks.length <= 1) return 0;

        int res = 0;
        for (int i = 1; i < stocks.length; i++) {
            BigDecimal expected = stocks[i - 1][3].multiply(PERCENTAGE_10).setScale(2, RoundingMode.HALF_UP);
            if (expected.compareTo(stocks[i][3]) == 0) res++;
        }
        return res;
    }

    /**
     * 计算股价最高最低值及日期
     */
    public StockInfoDTO calculateStockHighLowPriceInfo(List<SinaStock> stockList, StockInfoDTO dto) {
        BigDecimal highPrice = BIG_DECIMAL_ZERO;
        BigDecimal lowPrice = BIG_DECIMAL_MAX;
        int highIndex = 0, lowIndex = 0;

        for (int i = 0; i < stockList.size(); i++) {
            SinaStock s = stockList.get(i);
            if (s.getHigh().compareTo(highPrice) > 0) {
                highPrice = s.getHigh();
                highIndex = i;
            }
            if (s.getLow().compareTo(lowPrice) < 0) {
                lowPrice = s.getLow();
                lowIndex = i;
            }
        }

        dto.setHighestPrice(highPrice);
        dto.setLowestPrice(lowPrice);
        dto.setHighestDate(stockList.get(highIndex).getDay());
        dto.setLowestDate(stockList.get(lowIndex).getDay());
        return dto;
    }

    /**
     * 计算波动率
     */
    public double calculateStockPriceVolatility(BigDecimal highPrice, BigDecimal lowPrice) {
        return lowPrice.divide(highPrice, 2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * List转数组
     */
    private BigDecimal[][] listToArray(List<SinaStock> stockList) {
        return stockList.stream()
                .map(s -> new BigDecimal[]{s.getHigh(), s.getLow(), s.getOpen(), s.getClose()})
                .toArray(BigDecimal[][]::new);
    }

    /**
     * 构建股票信息DTO
     */
    public StockInfoDTO constructStockInfoDTO(StockInfoDTO dto) {
        List<SinaStock> list = getSinaStockData(dto.getStockSymbol());
        if (list.isEmpty()) {
            log.warn("无法获取股票数据: {}", dto.getStockSymbol());
            return dto;
        }

        dto = calculateStockHighLowPriceInfo(list, dto);
        BigDecimal[][] stocks = listToArray(list);

        dto.setDaysLimitUp(getDaysOfStockPriceLimitUp(stocks));
        dto.setConsecutiveDaysLimitUp(getConsecutiveDaysOfStockPriceLimitUp(stocks));
        dto.setVolatility(calculateStockPriceVolatility(dto.getHighestPrice(), dto.getLowestPrice()));

        return dto;
    }

    /**
     * 获取实时行情价格
     */
    public List<QtStock> getStockRealTimePrice(List<String> symbols) {
        String url = realTimeStockDataOfQtURL + stockStringService.formatUrlParameters(symbols);
        try {
            String body = restTemplate.getForObject(url, String.class);
            return Arrays.stream(body.split(Constants.SEMICOLON))
                    .map(stockStringService::parseQtStockString)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("获取腾讯实时股票数据失败", e);
            return Collections.emptyList();
        }
    }

    /**
     * 获取东方财富股票异动信息
     */
    public List<StockChange> getStockChangesFromEastMoney(int[] changeType) {
        log.info("开始获取东方财富股票异动接口信息...");

        int[] finalTypes = Optional.ofNullable(changeType)
                .filter(arr -> arr.length > 0)
                .orElse(new int[]{
                        8201, 8202, 8193, 4, 32, 64, 8207, 8209, 8211, 8213, 8215,
                        8204, 8203, 8194, 8, 16, 128, 8208, 8210, 8212, 8214, 8216
                });

        String typeParam = Arrays.stream(finalTypes)
                .mapToObj(String::valueOf)
                .collect(Collectors.joining(","));

        URI uri = UriComponentsBuilder.fromHttpUrl(stockChangesDataOfEastMoney)
                .queryParam("type", typeParam)
                .queryParam("pageindex", "0")
                .queryParam("pagesize", "10000")
                .queryParam("ut", "7eea3edcaed734bea9cbfc24409ed989")
                .queryParam("dpt", "wzchanges")
                .build(true)
                .toUri();

        HttpHeaders headers = new HttpHeaders();
        headers.set(HttpHeaders.USER_AGENT, "Mozilla/5.0");
        headers.setContentType(MediaType.APPLICATION_JSON);

        try {
            ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, new HttpEntity<>(headers), String.class);
            JSONObject root = JSON.parseObject(response.getBody());
            if (root == null || !root.containsKey("data")) return Collections.emptyList();

            JSONArray jsonArray = root.getJSONObject("data").getJSONArray("allstock");
            if (jsonArray == null) return Collections.emptyList();

            List<StockChange> list = jsonArray.toJavaList(StockChange.class).stream()
                    .filter(s -> {
                        String symbol = s.getStockSymbol();
                        String name = s.getStockName();
                        return (symbol.startsWith("60") || symbol.startsWith("00") ||
                                symbol.startsWith("30") || symbol.startsWith("68"))
                                && !name.startsWith("ST") && !name.startsWith("*ST");
                    })
                    .peek(s -> {
                        Date date = DateUtil.formatDate(s.getTm());
                        s.setId(s.getStockSymbol() + Constants.HYPHEN + s.getType() + Constants.HYPHEN + date.getTime());
                        s.setDate(date);
                        s.setChangeType(StockChangeEnum.fromValue(s.getType()).getChangeType());
                    })
                    .collect(Collectors.toList());

            log.info("获取股票异动信息结束，获取条数: {}", list.size());
            return list;
        } catch (Exception e) {
            log.error("获取东方财富股票异动信息失败", e);
            return Collections.emptyList();
        }
    }
}
